package com.algorithm.cn.stack;

/**
 * 
 * @author wangzonghui
 * @date 2021-12-28 22:18:30
 * @Description: 栈实现计算器  中缀表达式
 */
public class Calculator {

	public static void main(String[] args) {
		/**
		 * 根据思路完成表达式运算
		 */
		String expression="70+20*6-2";
		System.out.println(expression);
		ArrayStack2 numStack = new ArrayStack2(10);  //数字栈
		ArrayStack2 operStack = new ArrayStack2(10);  //符号栈
		
		int index=0;  //用于扫描
		int  num1=0;
		int  num2=0;
		int  oper=0;  //算法符
		int  res=0;  //结果
		char ch=' '; //存储每次扫描到的插入
		String keepNum="";  //用于拼接多位数
		while(true) {
			ch=expression.substring(index, index+1).charAt(0);
			
			//判断ch是什么，
			if(operStack.isOper(ch)) {   //如果是云算符
				//判断当前符号栈是否为空
				if(!operStack.isEmpty()) {
					//如果符号栈有操作符，就进行比较，如果当前的操作符优先级小于等于
					if(operStack.priority(ch)<=operStack.priority(operStack.peek())) {
						num1=numStack.pop();
						num2=numStack.pop();
						oper=operStack.pop();
						res=numStack.cal(num1, num2, oper);
						
						//计算结果入栈
						numStack.push(res);
						//操作符入栈
						operStack.push(ch);
					}else {
						operStack.push(ch);
					}
					
				}else {   //空直接入栈
					operStack.push(ch);
				}
				
			}else {  //如果为数字，则直接入数栈
				/**
				 * 分析思路
				 * 1、 当处理多位数时，不能发现一个数就立即入栈，因为他可能是多位数
				 * 2、 当处理数，需要向expression的表达式的index后再看一位，如果是数就进行扫描，符号时入栈
				 */
//				numStack.push(ch-48);
				//处理多位数
				keepNum+=ch;
				
				//判断是否为最后一位，最后一位返回
				if(index==expression.length()-1) {
					numStack.push(Integer.parseInt(keepNum));
					break;
				}
				//判断下一个字符是不是数字，是数字继续扫描，如果是云算符入栈。
				// index 值不变
				if(operStack.isOper(expression.subSequence(index+1, index+2).charAt(0))) {  //后一位是操作符
					numStack.push(Integer.parseInt(keepNum));
					keepNum="";
				}
				
			}
			
			//让index+1，并判断是否扫描到expression最后
			index++;
			if(index>=expression.length()) {
				break;
			}
		}

		//当表达式扫描结束，将顺序的从数栈和符号栈找那个pop出相应的数和符号并运行
		while(true) {
			if(operStack.isEmpty()) {
				break;
			}
			num1=numStack.pop();
			num2=numStack.pop();
			oper=operStack.pop();
			res=numStack.cal(num1, num2, oper);
			
			//计算结果入栈
			numStack.push(res);
			
		}
		System.out.println("最终结果为:"+numStack.pop());
	}

}


class ArrayStack2{
	private int maxSize;  //栈大小
	private int[] stack; //数组，模拟战
	private int top=-1; //top表示栈顶，初始化为-1
	
	public ArrayStack2(int maxSize) {
		this.maxSize=maxSize;
		stack=new int[this.maxSize];
	}
	
	//增加方法，返回栈顶数据，不是真正pop
	public  int peek() {
		return stack[top];
	}
	
	/**
	 * 栈满
	 * @return
	 */
	public boolean isFull() {
		return top==maxSize-1;
	}
	
	
	/**
	 * 栈空
	 */
	public boolean isEmpty() {
		return top==-1;
	}
	
	/**
	 * 入栈
	 * @param value
	 */
	public void push(int value) {
		if(isFull()) {
			System.out.println("栈满");
			return;
		}
		
		top++;
		stack[top]=value;
	}
	
	/**
	 * 出栈
	 * @return
	 */
	public int pop() {
		if(isEmpty()) {
			System.out.println("栈空");
			throw new RuntimeException("栈空，没有数据");
		}
		
		int value=stack[top];
		top--;
		return value;
	}
	
	/**
	 * 遍历栈
	 * 从栈顶开始
	 */
	public void list() {
		if(isEmpty()) {
			System.out.println("栈空，没有数据，无法遍历");
		}
		
		for(int i=top;i>=0;i--) {
			System.out.printf("stack[%d]=%d",i,stack[i]);
		}
	}
	
	/**
	 * 扩展功能，返回运算符优先级，优先级是程序员订的，优先级使用数字表示，数字越大优先级越高。
	 */
	public int priority(int oper) {
		if(oper=='*'||oper=='/') {
			return 1;
		}else if(oper=='+' || oper =='-') {
			return 0;
		}else {
			return -1;    //目前计算式只有+、-、* 、/
		}
	}
	
	/**
	 * 判断是否为运算符
	 * @param val
	 * @return 
	 */
	public boolean isOper(char val) {
		return val=='+' || val=='-'||val=='/'||val=='*';
	}
	
	/**
	 * 计算
	 * @param num1
	 * @param num2
	 * @param oper
	 * @return
	 */
	public int cal(int num1,int num2,int oper) {
		int res=0;  //存放计算结果
		switch(oper) {
		case '+':
			res=num1+num2;
			break;
		case '-':
			res =num2 -num1;  //注意顺序
			break;
		case '*':
			res =num2 *num1;  //注意顺序
			break;
		case '/':
			res =num2 /num1;  //注意顺序
			break;
		default:
			break;
		}
		return res;
	}
	
	
}

